#ifndef MATRIX3_H
#define MATRIX3_H

#include "Math/Math.h"

#include "Math/Vector3.h"
#include "Math/Vector4.h"
#include "Math/Matrix2.h"

#include <iostream>

namespace tyro
{
	namespace Math
	{

		template < class T >
		class Matrix3
		{

		public:

			/* Constructors */
			Matrix3<T>(){}
			inline 				Matrix3<T>( const T* mat ); 
			inline				Matrix3<T>( T _00, T _01, T _02, 
											T _10, T _11, T _12,
											T _20, T _21, T _22);
			inline				Matrix3<T>( const Matrix3<T>& mat );	


			/* Operator Overloaders */

			/* Casting Operators*/
			inline 				operator T* ();
			inline 				operator const T* () const;

			/* Access Operators */
			inline T&			operator () ( unsigned int row, unsigned int column );
			inline T			operator () ( unsigned int row, unsigned int column ) const;

			/* Assignment Operators */

			/* Relational and Equality Operators */
			inline bool			operator == ( const Matrix3<T>& mat ) const;
			inline bool			operator != ( const Matrix3<T>& mat ) const;

			/* Binary Arithmetic Operators */
			inline Matrix3<T> operator - () const;
			inline const Matrix3<T> operator + ( const Matrix2<T>& mat ) const;
			inline const Matrix3<T> operator + ( const Matrix3<T>& mat ) const;
			inline const Matrix3<T> operator - ( const Matrix2<T>& mat ) const;
			inline const Matrix3<T> operator - ( const Matrix3<T>& mat ) const;
			inline const Matrix3<T> operator * ( T value ) const;
			/*inline const Vector3<T> operator * ( const Vector3<T>& vec ) const;*/
			inline const Matrix3<T> operator * ( const Matrix2<T>& mat ) const;
			inline const Matrix3<T> operator * ( const Matrix3<T>& mat ) const;


			//-----------------------------------------------------------------------------
			// Friend multiply row Vector3 by matrix operator. W is assumed to be 1
			//-----------------------------------------------------------------------------
			inline friend const Vector3<T> operator * ( const Vector3<T>& vec, const Matrix3<T>& mat )
			{
				Vector3<T> ans;
				ans.x = vec.x * mat.m00 + vec.y * mat.m10 + vec.z * mat.m20;
				ans.y = vec.x * mat.m01 + vec.y * mat.m11 + vec.z * mat.m21;
				ans.z = vec.x * mat.m02 + vec.y * mat.m12 + vec.z * mat.m22;
				return ans;
			}

			inline friend const Vector3<T> operator * ( const Matrix3<T>& mat, const Vector3<T>& vec  )
			{
				Vector3<T> ans;
				ans.x = mat.m00 * vec.x + mat.m01 * vec.y + mat.m02 * vec.z;
				ans.y = mat.m10 * vec.x + mat.m11 * vec.y + mat.m12 * vec.z;
				ans.z = mat.m20 * vec.x + mat.m21 * vec.y + mat.m22 * vec.z;
				return ans;
			}

			//-----------------------------------------------------------------------------
			// Friend multiply row Vector4 by matrix operator.
			//-----------------------------------------------------------------------------
			inline friend const Vector4<T> operator * ( const Vector4<T>& vec, const Matrix3<T>& mat )
			{
				Vector4<T> ans;
				ans.x = vec.x * mat.m00 + vec.y * mat.m10 + vec.z * mat.m20;
				ans.y = vec.x * mat.m01 + vec.y * mat.m11 + vec.z * mat.m21;
				ans.z = vec.x * mat.m02 + vec.y * mat.m12 + vec.z * mat.m22;
				ans.w = 0.0f;
				return ans;
			}

			

			/* Compound Assignment Operators */
			inline Matrix3<T>&		operator += ( const Matrix2<T>& mat );
			inline Matrix3<T>&		operator += ( const Matrix3<T>& mat );
			inline Matrix3<T>&		operator -= ( const Matrix2<T>& mat );
			inline Matrix3<T>&		operator -= ( const Matrix3<T>& mat );
			inline Matrix3<T>&		operator *= ( T value );
			inline Matrix3<T>&		operator *= ( const Matrix2<T>& mat );
			inline Matrix3<T>&		operator *= ( const Matrix3<T>& mat );


			inline void Add( const Matrix3<T>& matA, const Matrix3<T>& matB );
			inline void Subtract( const Matrix3<T>& matA, const Matrix3<T>& matB );
			inline void	Multiply( const Matrix3<T>& matA, const Matrix3<T>& matB );
			inline void GetTranspose(Matrix3<T>& mat) const;

			/* Matrix 3 data members */
			union
			{
				T			m[ 3 ][ 3 ];
				T			mat[ 9 ];
				struct
				{
					T		m00, m01, m02;
					T		m10, m11, m12;
					T		m20, m21, m22;
				};
			};

			/* Constants */
			static const Matrix3<T> ZERO;
			static const Matrix3<T> IDENTITY;

		};


		template < class T >
		void tyro::Math::Matrix3<T>::GetTranspose( Matrix3<T>& mat ) const
		{
			mat.m00 = m00; mat.m01 = m10; mat.m02 = m20;
			mat.m10 = m01; mat.m11 = m11; mat.m12 = m21;
			mat.m20 = m02; mat.m21 = m12; mat.m22 = m22;
		}

		//-----------------------------------------------------------------------------
		// Constructor
		//-----------------------------------------------------------------------------
		template < class T >
		inline tyro::Math::Matrix3<T>::Matrix3( const T* mat ) 
		{
			memcpy( this->mat, mat, sizeof( this->mat ) );
		}

		//-----------------------------------------------------------------------------
		// Constructor
		//-----------------------------------------------------------------------------
		template < class T >
		inline	tyro::Math::Matrix3<T>::Matrix3( T _00, T _01, T _02, 
												T _10, T _11, T _12,
												T _20, T _21, T _22 )
		{
			( *this ).mat[ 0 ] = _00; ( *this ).mat[ 1 ] = _01; ( *this ).mat[ 2 ] = _02; 
			( *this ).mat[ 3 ] = _10; ( *this ).mat[ 4 ] = _11; ( *this ).mat[ 5 ] = _12; 
			( *this ).mat[ 6 ] = _20; ( *this ).mat[ 7 ] = _21; ( *this ).mat[ 8 ] = _22;
		}
		//-----------------------------------------------------------------------------
		// Constructor
		//-----------------------------------------------------------------------------
		template < class T >
		inline tyro::Math::Matrix3<T>::Matrix3( const Matrix3<T>& mat )
		{
			memcpy( this, &mat, sizeof( Matrix3<T> ) );
		}

		//-----------------------------------------------------------------------------
		// Casting operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix3<T>::operator T*()
		{
			return reinterpret_cast<T*>(this);
		}

		//-----------------------------------------------------------------------------
		// Const casting operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix3<T>::operator const T*() const
		{
			return reinterpret_cast<const T*>(this);
		}

		//-----------------------------------------------------------------------------
		// Access operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline T& Matrix3<T>::operator()( unsigned int row, unsigned int column )
		{
			assert( row < 3  &&  column < 3 );
			return m[ row ][ column ];
		}

		//-----------------------------------------------------------------------------
		// Const access operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline T Matrix3<T>::operator()( unsigned int row, unsigned int column ) const
		{
			assert( row < 3  &&  column < 3 );
			return m[ row ][ column ];
		}

		//-----------------------------------------------------------------------------
		// Equal to
		//-----------------------------------------------------------------------------
		template < class T >
		inline bool Matrix3<T>::operator == ( const Matrix3<T>& mat ) const
		{
			return 0 == memcmp( this, &mat, sizeof( Matrix3<T> ) );
		}

		//-----------------------------------------------------------------------------
		// Not Equal to
		//-----------------------------------------------------------------------------
		template < class T >
		inline bool Matrix3<T>::operator != ( const Matrix3<T>& mat ) const
		{
			return 0 != memcmp( this, &mat, sizeof( Matrix3<T> ) );
		}

		//-----------------------------------------------------------------------------
		// Unary minus
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix3<T> Matrix3<T>::operator- () const 
		{
			return Matrix3<T>(	-m00, -m01, -m02,
								-m10, -m11, -m12,
								-m20, -m21, -m22);
		}


		//-----------------------------------------------------------------------------
		// Addition operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix3<T> Matrix3<T>::operator + ( const Matrix2<T>& mat ) const
		{
			return Matrix3<T>(m00 + mat.m00, m01 + mat.m01, m02,
							  m10 + mat.m10, m11 + mat.m11, m12,
							  m20, m21, m22);
		}
		
		template < class T >
		inline const Matrix3<T> Matrix3<T>::operator + ( const Matrix3<T>& mat ) const
		{
			Matrix3<T> ans;
			ans.Add( *this, mat );
			return ans;
		}

		//-----------------------------------------------------------------------------
		// Matrix addition
		//-----------------------------------------------------------------------------
		template < class T >
		inline void Matrix3<T>::Add( const Matrix3<T>& matA, const Matrix3<T>& matB )
		{
			m00 = matA.m00 + matB.m00; m01 = matA.m01 + matB.m01; m02 = matA.m02 + matB.m02; 
			m10 = matA.m10 + matB.m10; m11 = matA.m11 + matB.m11; m12 = matA.m12 + matB.m12; 
			m20 = matA.m20 + matB.m20; m21 = matA.m21 + matB.m21; m22 = matA.m22 + matB.m22; 
		}

		//-----------------------------------------------------------------------------
		// Addition Assignment
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix3<T>& Matrix3<T>::operator += ( const Matrix2<T>& mat )
		{
			m00 += mat.m00 ; m01 += mat.m01 ;  
			m10 += mat.m10 ; m11 += mat.m11 ;  
			return *this;
		}

		template < class T >
		inline Matrix3<T>& Matrix3<T>::operator += ( const Matrix3<T>& mat )
		{
			m00 += mat.m00 ; m01 += mat.m01 ; m02 += mat.m02 ; 
			m10 += mat.m10 ; m11 += mat.m11 ; m12 += mat.m12 ; 
			m20 += mat.m20 ; m21 += mat.m21 ; m22 += mat.m22 ; 
			return *this;
		}

		//-----------------------------------------------------------------------------
		// Subtraction 
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix3<T> Matrix3<T>::operator - ( const Matrix2<T>& mat ) const
		{
			return Matrix3<T>(m00 - mat.m00, m01 - mat.m01, m02,
							  m10 - mat.m10, m11 - mat.m11, m12,
							  m20, m21, m22);
		}

		template < class T >
		inline const Matrix3<T> Matrix3<T>::operator - ( const Matrix3<T>& mat ) const
		{
			Matrix3<T> ans;
			ans.Subtract( *this, mat );
			return ans;
		}

		//-----------------------------------------------------------------------------
		// Matrix subtraction
		//-----------------------------------------------------------------------------
		template < class T >
		inline void Matrix3<T>::Subtract( const Matrix3<T>& matA, const Matrix3<T>& matB )
		{
			m00 = matA.m00 - matB.m00; m01 = matA.m01 - matB.m01; m02 = matA.m02 - matB.m02; 
			m10 = matA.m10 - matB.m10; m11 = matA.m11 - matB.m11; m12 = matA.m12 - matB.m12; 
			m20 = matA.m20 - matB.m20; m21 = matA.m21 - matB.m21; m22 = matA.m22 - matB.m22; 
		}

		//-----------------------------------------------------------------------------
		// Subtraction assignment operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix3<T>& Matrix3<T>::operator -= ( const Matrix2<T>& mat )
		{
			m00 -= mat.m00 ; m01 -= mat.m01 ; 
			m10 -= mat.m10 ; m11 -= mat.m11 ; 
			return *this;
		}

		template < class T >
		inline Matrix3<T>& Matrix3<T>::operator -= ( const Matrix3<T>& mat )
		{
			m00 -= mat.m00 ; m01 -= mat.m01 ; m02 -= mat.m02 ; 
			m10 -= mat.m10 ; m11 -= mat.m11 ; m12 -= mat.m12 ; 
			m20 -= mat.m20 ; m21 -= mat.m21 ; m22 -= mat.m22 ; 
			return *this;
		}

		//-----------------------------------------------------------------------------
		// Scalar multiplication operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix3<T> Matrix3<T>::operator * ( T value ) const
		{
			return Matrix3(	m00 * value , m01 * value , m02 * value , 
							m10 * value , m11 * value , m12 * value , 
							m20 * value , m21 * value , m22 * value );
		}

		//-----------------------------------------------------------------------------
		// Scalar multiplication Assignment
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix3<T>& Matrix3<T>::operator *= ( T value )
		{
			m00 *= value; m01 *= value; m02 *= value;
			m10 *= value; m11 *= value; m12 *= value;
			m20 *= value; m21 *= value; m22 *= value;
			return *this;
		}
		
		//-----------------------------------------------------------------------------
		// Matrix multiplication operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline Matrix3<T>& Matrix3<T>::operator *= ( const Matrix2<T>& mat )
		{
			*this = (*this) * mat;
			return *this;
		}
		
		template < class T >
		inline Matrix3<T>& Matrix3<T>::operator *= ( const Matrix3<T>& mat )
		{
			*this = (*this) * mat;
			return *this;
		}

		//-----------------------------------------------------------------------------
		// Matrix multiplication operator
		//-----------------------------------------------------------------------------
		template < class T >
		inline const Matrix3<T> Matrix3<T>::operator * ( const Matrix2<T>& mat ) const
		{
			return Matrix3<T>(m00 * mat.m00, m01 * mat.m01, m02,
							  m10 * mat.m10, m11 * mat.m11, m12,
							  m20, m21, m22);
		}

		template < class T >
		inline const Matrix3<T> Matrix3<T>::operator * ( const Matrix3<T>& mat ) const
		{
			Matrix3<T> ans;
			ans.Multiply( *this, mat );
			return ans;
		}

		

		//-----------------------------------------------------------------------------
		// Matrix multiplication
		//-----------------------------------------------------------------------------
		template < class T >
		inline void Matrix3<T>::Multiply( const Matrix3<T>& matA, const Matrix3<T>& matB )
		{
			m00 = matA.m00 * matB.m00; m01 = matA.m01 * matB.m01; m02 = matA.m02 * matB.m02; 
			m10 = matA.m10 * matB.m10; m11 = matA.m11 * matB.m11; m12 = matA.m12 * matB.m12; 
			m20 = matA.m20 * matB.m20; m21 = matA.m21 * matB.m21; m22 = matA.m22 * matB.m22; 
		}


		/*template < class T >
		inline const Vector3<T> Matrix3<T>::operator * ( const Vector3<T>& vec ) const
		{
			Vector3<T> ans;
			ans.x = vec.x * m00 + vec.y * m10 + vec.z * m20;
			ans.y = vec.x * m01 + vec.y * m11 + vec.z * m21;
			ans.z = vec.x * m02 + vec.y * m12 + vec.z * m22;
			return ans;
		}
		*/

	}
}


#endif